home *** CD-ROM | disk | FTP | other *** search
/ ETO Development Tools 4 / ETO Development Tools 4.iso / Tools - Objects / MPW C++ / MPW C++ 3.1 / Examples / CPlusExamples / TEDocument.cp < prev    next >
Text File  |  1990-09-11  |  24KB  |  818 lines

  1. /*------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    MultiFinder-Aware Simple TextEdit Sample Application
  6. #
  7. #    CPlusTESample
  8. #
  9. #    TEDocument.cp    -    C++ source
  10. #
  11. #    Copyright © 1989 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    
  15. #            1.10                     07/89
  16. #            1.00                     04/89
  17. #
  18. #    Components:
  19. #            CPlusTESample.make        July 9, 1989
  20. #            TApplicationCommon.h    July 9, 1989
  21. #            TApplication.h            July 9, 1989
  22. #            TDocument.h                July 9, 1989
  23. #            TECommon.h                July 9, 1989
  24. #            TESample.h                July 9, 1989
  25. #            TEDocument.h            July 9, 1989
  26. #            TApplication.cp            July 9, 1989
  27. #            TDocument.cp            July 9, 1989
  28. #            TESample.cp                July 9, 1989
  29. #            TEDocument.cp            July 9, 1989
  30. #            TESampleGlue.a            July 9, 1989
  31. #            TApplication.r            July 9, 1989
  32. #            TESample.r                July 9, 1989
  33. #
  34. #    CPlusTESample is an example application that demonstrates
  35. #    how to initialize the commonly used toolbox managers,
  36. #    operate successfully under MultiFinder, handle desk
  37. #    accessories and create, grow, and zoom windows. The
  38. #    fundamental TextEdit toolbox calls and TextEdit autoscroll
  39. #    are demonstrated. It also shows how to create and maintain
  40. #    scrollbar controls. 
  41. #
  42. #    This version of TESample has been substantially reworked in
  43. #    C++ to show how a "typical" object oriented program could
  44. #    be written. To this end, what was once a single source code
  45. #    file has been restructured into a set of classes which
  46. #    demonstrate the advantages of object-oriented programming.
  47. #
  48. ------------------------------------------------------------------------------*/
  49.  
  50.  
  51. /*
  52. Segmentation strategy:
  53.  
  54.     This program has only one segment, since the issues
  55.     surrounding segmentation within a class's methods have
  56.     not been investigated yet. We DO unload the data
  57.     initialization segment at startup time, which frees up
  58.     some memory 
  59.  
  60. SetPort strategy:
  61.  
  62.     Toolbox routines do not change the current port. In
  63.     spite of this, in this program we use a strategy of
  64.     calling SetPort whenever we want to draw or make calls
  65.     which depend on the current port. This makes us less
  66.     vulnerable to bugs in other software which might alter
  67.     the current port (such as the bug (feature?) in many
  68.     desk accessories which change the port on OpenDeskAcc).
  69.     Hopefully, this also makes the routines from this
  70.     program more self-contained, since they don't depend on
  71.     the current port setting. 
  72.  
  73. Clipboard strategy:
  74.  
  75.     This program does not maintain a private scrap.
  76.     Whenever a cut, copy, or paste occurs, we import/export
  77.     from the public scrap to TextEdit's scrap right away,
  78.     using the TEToScrap and TEFromScrap routines. If we did
  79.     use a private scrap, the import/export would be in the
  80.     activate/deactivate event and suspend/resume event
  81.     routines. 
  82. */
  83.  
  84. // Mac Includes
  85. #include <Types.h>
  86. #include <QuickDraw.h>
  87. #include <Fonts.h>
  88. #include <Events.h>
  89. #include <Controls.h>
  90. #include <Windows.h>
  91. #include <Menus.h>
  92. #include <TextEdit.h>
  93. #include <Dialogs.h>
  94. #include <Desk.h>
  95. #include <Scrap.h>
  96. #include <ToolUtils.h>
  97. #include <Memory.h>
  98. #include <SegLoad.h>
  99. #include <Files.h>
  100. #include <OSUtils.h>
  101. #include <Traps.h>
  102.  
  103. #include "TEDocument.h"
  104. #include "TESample.h"
  105.  
  106. extern "C" { 
  107.     // prototypes for functions that don't belong to any one class
  108.     pascal ClikLoopProcPtr GetOldClikLoop(void);
  109.     pascal void PascalClikLoop(void);
  110.     void CommonAction(ControlHandle control,short* amount);
  111.     pascal void VActionProc(ControlHandle control,short part);
  112.     pascal void HActionProc(ControlHandle control,short part);
  113.     // this routine is written in Assembler, since it needs to tweak registers
  114.     pascal void ASMCLIKLOOP();
  115. };
  116.  
  117. // kTextMargin is the number of pixels we leave blank at the edge of the window.
  118. const short kTextMargin = 2;
  119.  
  120. // kMaxDocWidth is an arbitrary number used to specify the width of the TERec's
  121. // destination rectangle so that word wrap and horizontal scrolling can be
  122. // demonstrated.
  123. const short    kMaxDocWidth = 576;
  124.     
  125. // kMinDocDim is used to limit the minimum dimension of a window when GrowWindow
  126. // is called.
  127. const short    kMinDocDim = 64;
  128.     
  129. // kMaxTELength is an arbitrary number used to limit the length of text in the TERec
  130. // so that various errors won't occur from too many characters being in the text.
  131. const short    kMaxTELength = 32000;
  132.  
  133. // kControlInvisible is used the same way to 'turn on' the control.
  134. const short kControlVisible = 0xFF;
  135.  
  136. // ScrollBarAdjust, GrowBoxAdjust, and ScrollBar width are used in calculating
  137. // values for control positioning and sizing.
  138. const short kScrollbarAdjust = 15;
  139. const short kGrowboxAdjust = 15;
  140. const short kScrollbarWidth = 16;
  141.  
  142. // kTESlop provides some extra security when pre-flighting edit commands.
  143. const short kTESlop = 1024;
  144.  
  145. // kScrollTweek compensates for off-by-one requirements of the scrollbars
  146. // to have borders coincide with the growbox.
  147. const short kScrollTweek = 2;
  148.     
  149. // kCrChar is used to match with a carriage return when calculating the
  150. // number of lines in the TextEdit record. kDelChar is used to check for
  151. // delete in keyDowns.
  152. const short kCrChar = 13;
  153. const short kDelChar = 8;
  154.  
  155. // notice that we pass the resID parameter up to our base class,
  156. // which actually creates the window for us
  157. TEDocument::TEDocument(short resID)    : (resID)
  158. {
  159.     Boolean good;
  160.     Rect destRect, viewRect;
  161.  
  162.     good = false;
  163.     SetPort(fDocWindow);
  164.     GetTERect(&viewRect);
  165.     destRect = viewRect;
  166.     destRect.right = destRect.left + kMaxDocWidth;
  167.     fDocTE = TENew(&destRect, &viewRect);
  168.     
  169.     good = fDocTE != nil;    // if TENew succeeded, we have a good document 
  170.  
  171.     if ( good )
  172.       {
  173.         // set up TE record
  174.         AdjustViewRect();
  175.         TEAutoView(true, fDocTE);
  176.         fDocClik = (*fDocTE)->clikLoop;
  177.         (*fDocTE)->clikLoop = (ClikLoopProcPtr) ASMCLIKLOOP;
  178.  
  179.         // get vertical scrollbar
  180.         fDocVScroll = GetNewControl(rVScroll, fDocWindow);
  181.         good = (fDocVScroll != nil);
  182.       }
  183.     if ( good)
  184.       {
  185.         fDocHScroll = GetNewControl(rHScroll, fDocWindow);
  186.         good = (fDocHScroll != nil);
  187.       }
  188.     
  189.     if ( good )                // good? — adjust & draw the controls, draw the window
  190.       {
  191.         AdjustScrollValues(true);
  192.         ShowWindow(fDocWindow);
  193.       }
  194.     else
  195.       {
  196.         // tell user we failed
  197.         AlertUser(kTEDocErrStrings,eNoWindow); 
  198.       }
  199. }
  200.  
  201. // At this point, if there was a document associated with a
  202. // window, you could do any document saving processing if it is 'dirty'.
  203. // DoCloseWindow would return true if the window actually closed, i.e.,
  204. // the user didn’t cancel from a save dialog. This result is handy when
  205. // the user quits an application, but then cancels the save of a document
  206. // associated with a window.
  207.  
  208. TEDocument::~TEDocument(void)
  209. {
  210.     HideWindow(fDocWindow);
  211.     if ( fDocTE != nil )
  212.       {
  213.         TEDispose(fDocTE);            // dispose the TEHandle if we got far enough to make one 
  214.       }
  215.     if ( fDocVScroll != nil )
  216.       {
  217.         DisposeControl(fDocVScroll);
  218.       }
  219.     if ( fDocHScroll != nil )
  220.       {
  221.         DisposeControl(fDocHScroll);
  222.       }
  223.     // base class destructor will dispose of window
  224. }
  225.  
  226. void TEDocument::DoZoom(short partCode)
  227. {
  228.     Rect tRect;
  229.  
  230.     tRect = fDocWindow->portRect;
  231.     EraseRect(&tRect);
  232.     ZoomWindow(fDocWindow, partCode, fDocWindow == FrontWindow());
  233.     AdjustScrollbars(true);        // adjust, redraw anyway 
  234.     AdjustTE();
  235.     InvalRect(&tRect);            // invalidate the whole content 
  236.     // the scrollbars were taken care of by AdjustScrollbars, so validate ’em 
  237.     tRect = (*fDocVScroll)->contrlRect;
  238.     ValidRect(&tRect);
  239.     tRect = (*fDocHScroll)->contrlRect;
  240.     ValidRect(&tRect);
  241. }
  242.  
  243. // Called when a mouseDown occurs in the grow box of an active window. 
  244.  
  245. void TEDocument::DoGrow(EventRecord* theEvent)
  246. {
  247.     long growResult;
  248.     Rect tRect, tRect2;
  249.     
  250.     tRect = qd.screenBits.bounds;
  251.     tRect.left = kMinDocDim;
  252.     tRect.top = kMinDocDim;
  253.     growResult = GrowWindow(fDocWindow, theEvent->where, &tRect);
  254.     // see if it really changed size 
  255.     if ( growResult != 0 )
  256.       {
  257.         tRect = (*fDocTE)->viewRect;
  258.         SizeWindow(fDocWindow, LoWrd(growResult), HiWrd(growResult), true);
  259.         AdjustScrollbars(true);
  260.         AdjustTE();
  261.         // calculate & validate the region that hasn’t changed so it won’t get redrawn
  262.         // Note: we copy rectangles so that we don't take address of object fields.
  263.         tRect2 = (*fDocTE)->viewRect;
  264.         (void) SectRect(&tRect, &tRect2, &tRect);
  265.         tRect2 = fDocWindow->portRect; InvalRect(&tRect2);
  266.         ValidRect(&tRect);
  267.         tRect2 = (*fDocVScroll)->contrlRect; ValidRect(&tRect2);
  268.         tRect2 = (*fDocHScroll)->contrlRect; ValidRect(&tRect2);
  269.       }
  270. }
  271.  
  272. void TEDocument::DoContent(EventRecord* theEvent)
  273. {
  274.     Point mouse;
  275.     ControlHandle control;
  276.     short part, value;
  277.     Boolean shiftDown;
  278.     Rect teRect;
  279.  
  280.     SetPort(fDocWindow);
  281.     mouse = theEvent->where;                            // get the click position 
  282.     GlobalToLocal(&mouse);
  283.     GetTERect(&teRect);
  284.     if ( PtInRect(mouse, &teRect) )
  285.       {
  286.         /* see if we need to extend the selection */
  287.         shiftDown = (theEvent->modifiers & shiftKey) != 0;    /* extend if Shift is down */
  288.         TEClick(mouse, shiftDown, fDocTE);
  289.       }
  290.     else
  291.       {
  292.         part = FindControl(mouse, fDocWindow, &control);
  293.         switch ( part )
  294.           {
  295.             case 0:
  296.                 // do nothing if not in a control
  297.                 break;
  298.             case inThumb:
  299.                 value = GetCtlValue(control);
  300.                 part = TrackControl(control, mouse, nil);
  301.                 if ( part != 0 )
  302.                   {
  303.                     value -= GetCtlValue(control);
  304.                     // value now has CHANGE in value; if value changed, scroll 
  305.                     if ( value != 0 )
  306.                         if ( control == fDocVScroll )
  307.                             TEScroll(0, value * (*fDocTE)->lineHeight, fDocTE);
  308.                         else TEScroll(value, 0, fDocTE);
  309.                   }
  310.                 break;
  311.             default:                        // they clicked in an arrow, so track & scroll 
  312.                 if ( control == fDocVScroll )
  313.                     value = TrackControl(control, mouse, (ProcPtr) VActionProc);
  314.                 else value = TrackControl(control, mouse, (ProcPtr) HActionProc);
  315.                 break;
  316.           }
  317.       }
  318. }
  319.  
  320. void TEDocument::DoKeyDown(EventRecord* theEvent)
  321. {
  322.     char key;
  323.  
  324.     if (theEvent->modifiers & cmdKey)    // don't process command characters
  325.       return;
  326.     key = (char) (theEvent->message & charCodeMask);
  327.     // we have a char. for our window; see if we are still below TextEdit’s
  328.     // limit for the number of characters
  329.     if ((key == kDelChar) ||
  330.         ((*fDocTE)->teLength - ((*fDocTE)->selEnd - (*fDocTE)->selStart) + 1 < kMaxTELength) )
  331.       {
  332.         TEKey(key, fDocTE);
  333.         AdjustScrollbars(false);
  334.         AdjustTE();
  335.       }
  336.     else AlertUser(kTEDocErrStrings,eExceedChar);
  337. }
  338.  
  339. void TEDocument::DoActivate(Boolean becomingActive)
  340. {
  341.     if ( becomingActive )
  342.       {
  343.         RgnHandle    tempRgn;
  344.         RgnHandle    clipRgn;
  345.         Rect        growRect;
  346.         Rect        tRect;
  347.  
  348.         // since we don’t want TEActivate to draw a selection in an area where
  349.         // we’re going to erase and redraw, we’ll clip out the update region
  350.         // before calling it.
  351.         tempRgn = NewRgn();
  352.         clipRgn = NewRgn();
  353.         // save old update region
  354.         CopyRgn(((WindowPeek) fDocWindow)->updateRgn, tempRgn);
  355.         // put it in local coords
  356.         OffsetRgn(tempRgn, fDocWindow->portBits.bounds.left, fDocWindow->portBits.bounds.top);
  357.         GetClip(clipRgn);
  358.         // subtract updateRgn from clipRgn
  359.         DiffRgn(clipRgn, tempRgn, tempRgn);
  360.         // make it the new clipRgn
  361.         SetClip(tempRgn);
  362.         TEActivate(fDocTE);
  363.         // restore the full-blown clipRgn
  364.         SetClip(clipRgn);
  365.         // get rid of temp regions
  366.         DisposeRgn(tempRgn);
  367.         DisposeRgn(clipRgn);
  368.  
  369.         /* the controls must be redrawn on activation: */
  370.         (*fDocVScroll)->contrlVis = kControlVisible;
  371.         (*fDocHScroll)->contrlVis = kControlVisible;
  372.         // copy rectangles to avoid unsafe object field references!
  373.         tRect = (*fDocVScroll)->contrlRect; InvalRect(&tRect);
  374.         tRect = (*fDocHScroll)->contrlRect; InvalRect(&tRect);
  375.         // the growbox needs to be redrawn on activation:
  376.         growRect = fDocWindow->portRect;
  377.         // adjust for the scrollbars
  378.         growRect.top = growRect.bottom - kScrollbarAdjust;
  379.         growRect.left = growRect.right - kScrollbarAdjust;
  380.         InvalRect(&growRect);
  381.       }
  382.     else
  383.       {        
  384.         TEDeactivate(fDocTE);
  385.         /* the controls must be hidden on deactivation: */
  386.         HideControl(fDocVScroll);
  387.         HideControl(fDocHScroll);
  388.         // we draw grow icon immediately, since we deactivate controls
  389.         // immediately, and the update delay looks funny
  390.         DrawGrowIcon(fDocWindow);
  391.       }
  392. }
  393.  
  394. void TEDocument::DoUpdate(void)
  395. {
  396.     BeginUpdate(fDocWindow);                // this sets up the visRgn 
  397.     if ( ! EmptyRgn(fDocWindow->visRgn) )    // draw if updating needs to be done 
  398.       {
  399.         DrawWindow();
  400.       }
  401.     EndUpdate(fDocWindow);
  402. }
  403.  
  404. // calculate how much idle time we need
  405.  
  406. unsigned long TEDocument::CalcIdle(void)
  407. {
  408.     if (HaveSelection())
  409.       return GetCaretTime();
  410.     else return kMaxSleepTime;    // if we don't have a selection, we don't need to idle
  411. }
  412.  
  413. // This is called whenever we get a null event et al.
  414. // It takes care of necessary periodic actions. For this program,
  415. // it calls TEIdle.
  416.  
  417. void TEDocument::DoIdle(void)
  418. {
  419.     TEIdle(fDocTE);
  420. } // DoIdle
  421.  
  422. // Draw the contents of an application window. 
  423.  
  424. void TEDocument::DrawWindow(void)
  425. {
  426.     Rect tRect;
  427.  
  428.     SetPort(fDocWindow);
  429.     tRect = fDocWindow->portRect;
  430.     EraseRect(&tRect);
  431.     TEUpdate(&tRect, fDocTE);
  432.     DrawControls(fDocWindow);
  433.     DrawGrowIcon(fDocWindow);
  434. } // DrawWindow
  435.  
  436. // Return a rectangle that is inset from the portRect by the size of
  437. // the scrollbars and a little extra margin.
  438.  
  439. void TEDocument::GetTERect(Rect* teRect)
  440. {
  441.     *teRect = fDocWindow->portRect;
  442.     InsetRect(teRect, kTextMargin, kTextMargin);            // adjust for margin 
  443.     teRect->bottom = teRect->bottom - kScrollbarAdjust;    // and for the scrollbars 
  444.     teRect->right = teRect->right - kScrollbarAdjust;
  445. } // GetTERect
  446.  
  447. // setup a region which contains the visible text
  448.  
  449. void TEDocument::GetVisTERgn(RgnHandle rgn)
  450. {
  451.     Rect teRect;
  452.  
  453.     teRect = (*fDocTE)->viewRect;    // get a local copy of viewRect
  454.     SetPort(fDocWindow);            // make sure we have right port
  455.     LocalToGlobal(&TopLeft(teRect));
  456.     LocalToGlobal(&BotRight(teRect));
  457.     RectRgn(rgn, &teRect);
  458.     // we temporarily change the port’s origin to “globalfy” the visRgn
  459.     SetOrigin(-(fDocWindow->portBits.bounds.left),
  460.               -(fDocWindow->portBits.bounds.top));
  461.     SectRgn(rgn, fDocWindow->visRgn, rgn);
  462.     SetOrigin(0, 0);
  463. } // GetTERgn
  464.  
  465. // Return boolean value indicating that there is or is not a
  466. // selection in the document
  467.  
  468. Boolean TEDocument::HaveSelection()
  469. {
  470.     if ( (*fDocTE)->selStart < (*fDocTE)->selEnd )
  471.       return true;
  472.     else return false;
  473. }
  474.  
  475. // Update the TERec's view rect so that it is the greatest multiple of
  476. // the lineHeight that still fits in the old viewRect.
  477.  
  478. void TEDocument::AdjustViewRect(void)
  479. {
  480.     TEPtr te;
  481.     
  482.     te = *fDocTE;
  483.     te->viewRect.bottom = (((te->viewRect.bottom - te->viewRect.top) / te->lineHeight)
  484.                             * te->lineHeight) + te->viewRect.top;
  485. } // AdjustViewRect
  486.  
  487. // Scroll the TERec around to match up to the potentially updated scrollbar
  488. // values. This is really useful when the window has been resized such that the
  489. // scrollbars became inactive but the TERec was already scrolled.
  490.  
  491. void TEDocument::AdjustTE(void)
  492. {
  493.     TEPtr te;
  494.     
  495.     te = *fDocTE;
  496.     TEScroll((te->viewRect.left - te->destRect.left) - GetCtlValue(fDocHScroll),
  497.              (te->viewRect.top - te->destRect.top) - 
  498.                  (GetCtlValue(fDocVScroll) * te->lineHeight),
  499.              fDocTE);
  500. } // AdjustTE
  501.  
  502. // Re-calculate the position and size of the viewRect and the scrollbars.
  503. // kScrollTweek compensates for off-by-one requirements of the scrollbars
  504. // to have borders coincide with the growbox.
  505.  
  506. void TEDocument::AdjustScrollSizes(void)
  507. {
  508.     Rect teRect;
  509.     
  510.     GetTERect(&teRect);
  511.     (*fDocTE)->viewRect = teRect;
  512.     AdjustViewRect();
  513.     MoveControl(fDocVScroll, fDocWindow->portRect.right - kScrollbarAdjust, -1);
  514.     SizeControl(fDocVScroll, kScrollbarWidth,
  515.                 fDocWindow->portRect.bottom - fDocWindow->portRect.top -
  516.                     kGrowboxAdjust + kScrollTweek);
  517.     MoveControl(fDocHScroll, -1, fDocWindow->portRect.bottom - kScrollbarAdjust);
  518.     SizeControl(fDocHScroll,
  519.                 fDocWindow->portRect.right - fDocWindow->portRect.left -
  520.                     kGrowboxAdjust + kScrollTweek,
  521.                 kScrollbarWidth);
  522. } // AdjustScrollSizes
  523.  
  524. // Turn off the controls by jamming a zero into their contrlVis fields (HideControl erases them
  525. // and we don't want that). If the controls are to be resized as well, call the procedure to do that,
  526. // then call the procedure to adjust the maximum and current values. Finally re-enable the controls
  527. // by jamming a $FF in their contrlVis fields (ShowControl re-draws the control, which may not be
  528. // necessary).
  529.  
  530. void TEDocument::AdjustScrollbars(Boolean needsResize)
  531. {
  532.     // First, turn visibility of scrollbars off so we won’t get unwanted redrawing 
  533.     (*fDocVScroll)->contrlVis = 0;
  534.     (*fDocHScroll)->contrlVis = 0;
  535.     if ( needsResize )
  536.       AdjustScrollSizes();
  537.     AdjustScrollValues(needsResize);
  538.     // Now, restore visibility in case we never had to draw during adjustment 
  539.     (*fDocVScroll)->contrlVis = 0xff;
  540.     (*fDocHScroll)->contrlVis = 0xff;
  541. } // AdjustScrollbars 
  542.  
  543. // Calculate the new control maximum value and current value, whether it is the horizontal or
  544. // vertical scrollbar. The vertical max is calculated by comparing the number of lines to the
  545. // vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document
  546. // width to the width of the viewRect. The current values are set by comparing the offset between
  547. // the view and destination rects. If necessary, redraw the control by calling ShowControl.
  548.  
  549. void TEDocument::AdjustHV(Boolean isVert,Boolean mustRedraw)
  550. {
  551.     short value, lines, max;
  552.     short oldValue, oldMax;
  553.     TEPtr te;
  554.     ControlHandle control;
  555.  
  556.     if (isVert)
  557.       control = fDocVScroll;
  558.     else control = fDocHScroll;
  559.     oldValue = GetCtlValue(control);
  560.     oldMax = GetCtlMax(control);
  561.     te = *fDocTE;                            // point to TERec for convenience 
  562.     if ( isVert )
  563.       {
  564.         lines = te->nLines;
  565.         // since nLines isn’t right if the last character is a return, check for that case
  566.         if ( *(*te->hText + te->teLength - 1) == kCrChar )
  567.           lines += 1;
  568.         max = lines - ((te->viewRect.bottom - te->viewRect.top) /
  569.                        te->lineHeight);
  570.       }
  571.     else max = kMaxDocWidth - (te->viewRect.right - te->viewRect.left);
  572.     
  573.     if ( max < 0 )
  574.       max = 0;
  575.     SetCtlMax(control, max);
  576.     
  577.     // Must deref. after SetCtlMax since, technically, it could draw and therefore move
  578.     // memory. This is why we don’t just do it once at the beginning.
  579.     te = *fDocTE;
  580.     if ( isVert )
  581.       value = (te->viewRect.top - te->destRect.top) / te->lineHeight;
  582.     else value = te->viewRect.left - te->destRect.left;
  583.     
  584.     if ( value < 0 )
  585.       value = 0;
  586.     else if ( value >  max )
  587.       value = max;
  588.     
  589.     SetCtlValue(control, value);
  590.     // now redraw the control if asked to or if a setting changed 
  591.     if ( mustRedraw || (max != oldMax) || (value != oldValue) )
  592.         ShowControl(control);
  593. } // AdjustHV
  594.  
  595. // Simply call the common adjust routine for the vertical and horizontal scrollbars. 
  596.  
  597. void TEDocument::AdjustScrollValues(Boolean mustRedraw)
  598. {
  599.     AdjustHV(true, mustRedraw);
  600.     AdjustHV(false, mustRedraw);
  601. } // AdjustScrollValues
  602.  
  603. ClikLoopProcPtr TEDocument::GetClikLoop(void)
  604. {
  605.     return fDocClik;
  606. }
  607.  
  608. TEHandle TEDocument::GetTEHandle(void)
  609. {
  610.     return fDocTE;
  611. }
  612.  
  613. void TEDocument::DoCut(void)
  614. {
  615.     long total, contig;
  616.  
  617.     if (ZeroScrap() == noErr)
  618.       {
  619.         PurgeSpace(&total, &contig);
  620.         if ((*fDocTE)->selEnd - (*fDocTE)->selStart + kTESlop > contig)
  621.           AlertUser(kTEDocErrStrings,eNoSpaceCut);
  622.         else
  623.           {
  624.             TECut(fDocTE);
  625.             if (TEToScrap() != noErr)
  626.               {
  627.                 AlertUser(kTEDocErrStrings,eNoCut);
  628.                 (void) ZeroScrap();
  629.               }
  630.           }
  631.       }
  632.     AdjustScrollbars(false);
  633.     AdjustTE();
  634. }
  635.  
  636. void TEDocument::DoCopy(void)
  637. {
  638.     if ( ZeroScrap() == noErr )
  639.       {
  640.         TECopy(fDocTE);                // after copying, export the TE scrap
  641.         if ( TEToScrap() != noErr )
  642.           {
  643.             AlertUser(kTEDocErrStrings,eNoCopy);
  644.             ZeroScrap();
  645.           }
  646.       }
  647.     AdjustScrollbars(false);
  648.     AdjustTE();
  649. }
  650.  
  651. void TEDocument::DoPaste(void)
  652. {
  653.     Handle aHandle;
  654.     long oldSize, newSize;
  655.     OSErr saveErr;
  656.  
  657.     if ( TEFromScrap() == noErr )
  658.       {
  659.         if ( TEGetScrapLen() + ((*fDocTE)->teLength -
  660.              ((*fDocTE)->selEnd - (*fDocTE)->selStart)) > kMaxTELength )
  661.           AlertUser(kTEDocErrStrings,eExceedPaste);
  662.         else
  663.           {
  664.             aHandle = (Handle) TEGetText(fDocTE);
  665.             oldSize = GetHandleSize(aHandle);
  666.             newSize = oldSize + TEGetScrapLen() + kTESlop;
  667.             SetHandleSize(aHandle, newSize);
  668.             saveErr = MemError();
  669.             SetHandleSize(aHandle, oldSize);
  670.             if (saveErr != noErr)
  671.               AlertUser(kTEDocErrStrings,eNoSpacePaste);
  672.             else TEPaste(fDocTE);
  673.           }
  674.       }
  675.     else AlertUser(kTEDocErrStrings,eNoPaste);
  676.     AdjustScrollbars(false);
  677.     AdjustTE();
  678. }
  679.  
  680. void TEDocument::DoClear(void)
  681. {
  682.     TEDelete(fDocTE);
  683.     AdjustScrollbars(false);
  684.     AdjustTE();
  685. }
  686.  
  687. /*
  688.     Routines used by this class, which don't belong to the class since we use
  689.     them as toolbox filter routines, and you cannot pass class methods as ProcPtrs.
  690. */
  691.  
  692. // we refer back to the owning application so that we can get access to
  693. // the document list, to find the current document object.
  694. extern TESample *gTheApplication;
  695.  
  696. // Common algorithm for pinning the value of a control. It returns the actual amount
  697. // the value of the control changed.
  698.  
  699. void CommonAction(ControlHandle control,short* amount)
  700. {
  701.     short        value, max;
  702.     
  703.     value = GetCtlValue(control);
  704.     max = GetCtlMax(control);
  705.     *amount = value - *amount;
  706.     if ( *amount <= 0 )
  707.         *amount = 0;
  708.     else if ( *amount >= max )
  709.         *amount = max;
  710.     SetCtlValue(control, *amount);
  711.     *amount = value - *amount;
  712. } // CommonAction 
  713.  
  714.  
  715. // Determines how much to change the value of the vertical scrollbar by and how
  716. // much to scroll the TE record.
  717.  
  718. pascal void VActionProc(ControlHandle control,short part)
  719. {
  720.     short        amount;
  721.     WindowPtr    window;
  722.     TEPtr        te;
  723.     TEDocument* doc;
  724.  
  725.     if ( part != 0 )
  726.       {
  727.         window = (*control)->contrlOwner;
  728.         doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(window);
  729.         te = *(doc->GetTEHandle());
  730.         switch ( part )
  731.           {
  732.             case inUpButton:
  733.             case inDownButton:        // one line 
  734.                 amount = 1;
  735.                 break;
  736.             case inPageUp:            // one page 
  737.             case inPageDown:
  738.                 amount = (te->viewRect.bottom - te->viewRect.top) / te->lineHeight;
  739.                 break;
  740.           }
  741.         if ( (part == inDownButton) || (part == inPageDown) )
  742.             amount = -amount;        // reverse direction for a downer 
  743.         CommonAction(control, &amount);
  744.         if ( amount != 0 )
  745.             TEScroll(0, amount * te->lineHeight, doc->GetTEHandle());
  746.       }
  747. } // VActionProc 
  748.  
  749. // Determines how much to change the value of the horizontal scrollbar by and how
  750. // much to scroll the TE record.
  751.  
  752. pascal void HActionProc(ControlHandle control,short part)
  753. {
  754.     short        amount;
  755.     WindowPtr    window;
  756.     TEPtr        te;
  757.     TEDocument* doc;
  758.  
  759.     if ( part != 0 )
  760.       {
  761.         window = (*control)->contrlOwner;
  762.         doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(window);
  763.         te = *(doc->GetTEHandle());
  764.         switch ( part )
  765.           {
  766.             case inUpButton:
  767.             case inDownButton:        // a few pixels 
  768.                 amount = 4;
  769.                 break;
  770.             case inPageUp:            // a page 
  771.             case inPageDown:
  772.                 amount = te->viewRect.right - te->viewRect.left;
  773.                 break;
  774.           }
  775.         if ( (part == inDownButton) || (part == inPageDown) )
  776.             amount = -amount;        // reverse direction 
  777.         CommonAction(control, &amount);
  778.         if ( amount != 0 )
  779.             TEScroll(amount, 0, doc->GetTEHandle());
  780.       }
  781. } // VActionProc 
  782.  
  783. // Gets called from our assembly language routine, AsmClikLoop, which is in
  784. // turn called by the TEClick toolbox routine. Saves the windows clip region,
  785. // sets it to the portRect, adjusts the scrollbar values to match the TE scroll
  786. // amount, then restores the clip region.
  787.  
  788. pascal void PascalClikLoop(void)
  789. {
  790.     RgnHandle    region;
  791.     WindowPtr wind;
  792.     TEDocument* doc;
  793.  
  794.     wind = FrontWindow();
  795.     doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(wind);
  796.     region = NewRgn();
  797.     GetClip(region);    // save clip 
  798.     ClipRect(&wind->portRect);
  799.     doc->AdjustScrollValues(false);
  800.     SetClip(region);    // restore clip 
  801.     DisposeRgn(region);
  802. } // PascalClikLoop 
  803.  
  804. // Gets called from our assembly language routine, AsmClikLoop, which is in
  805. // turn called by the TEClick toolbox routine. It returns the address of the
  806. // default clikLoop routine that was put into the TERec by TEAutoView to
  807. // AsmClikLoop so that it can call it.
  808.  
  809. pascal ClikLoopProcPtr GetOldClikLoop(void)
  810. {
  811.     TEDocument* doc;
  812.  
  813.     doc = (TEDocument*) (gTheApplication->DocList())->FindDoc(FrontWindow());
  814.     if (doc == nil)
  815.       return nil;
  816.     return doc->GetClikLoop();
  817. } // GetOldClikLoop
  818.